#pragma warning(disable:4996)
#pragma warning(disable:4101)
#include "config.h"

#include <fstream>
#include <sstream>

#include "rapidjson-1.1.0/include/rapidjson/document.h"
#include "rapidjson-1.1.0/include/rapidjson/filereadstream.h"
#include "rapidjson-1.1.0/include/rapidjson/prettywriter.h"
#include "rapidjson-1.1.0/include/rapidjson/rapidjson.h"
#include "rapidjson-1.1.0/include/rapidjson/stringbuffer.h"
#include "rapidjson-1.1.0/include/rapidjson/writer.h"

bool CZlBaseConfig::Load(const std::string& path)
{
    try
    {
        std::ifstream ifs;
        ifs.open(path, std::ifstream::in);
        std::stringstream buffer;
        buffer << ifs.rdbuf();
        ifs.close();

        rapidjson::Document dom;
        dom.Parse<0>(buffer.str().c_str());
        if (dom.HasParseError() || !dom.IsObject())
        {
            return false;
        }

        devices_.clear();
        auto device_array = dom["devices"].GetArray();
        for (const auto& obj : device_array)
        {
            if (!obj.IsObject())
            {
                return false;
            }

            ZLBaseDevice device;
            device.number = obj.HasMember("number") ? obj["number"].GetInt() : 0;
            device.count = obj.HasMember("count") ? obj["count"].GetInt() : 0;
            device.type = zl::zlbase::EDeviceType(obj.HasMember("type") ? obj["type"].GetInt() : 0);
            device.uuid = obj.HasMember("uuid") ? obj["uuid"].GetString() : "";
            if (!obj.HasMember("datalink") || !obj["datalink"].IsObject())
            {
                return false;
            }

            auto datalink_obj = obj["datalink"].GetObject();
            auto type = datalink_obj.HasMember("type") ? datalink_obj["type"].GetInt() : 0;
            auto is_shared = datalink_obj.HasMember("is_shared") ? datalink_obj["is_shared"].GetInt() : 0;
            device.datalink.type = type;
            device.datalink.is_shared = (is_shared == 1);
            switch (type)
            {
                case zl::zlbase::EDatalinkType_EthernetUdp:
                {
                    device.datalink.localport = datalink_obj.HasMember("localport") ? datalink_obj["localport"].GetInt() : 30000;
                    std::string tmp1 = datalink_obj.HasMember("remoteaddr") ? datalink_obj["remoteaddr"].GetString() : "127.0.0.1";
                    strcpy(device.datalink.remoteaddr, tmp1.c_str());
                    device.datalink.remoteport = datalink_obj.HasMember("remoteport") ? datalink_obj["remoteport"].GetInt() : 30000;
                    break;
                }
                case zl::zlbase::EDatalinkType_Serialport:
                {
                    std::string tmp1 = datalink_obj.HasMember("com") ? datalink_obj["com"].GetString() : "";
                    strcpy(device.datalink.com, tmp1.c_str());
                    device.datalink.baud_rate = datalink_obj.HasMember("baud_rate") ? datalink_obj["baud_rate"].GetInt() : 9600;
                    if (datalink_obj.HasMember("flow_control"))
                    {
                        device.datalink.flow_control = datalink_obj["flow_control"].GetInt();
                    }
                    if (datalink_obj.HasMember("parity"))
                    {
                        device.datalink.parity = datalink_obj["parity"].GetInt();
                    }
                    if (datalink_obj.HasMember("stop_bits"))
                    {
                        device.datalink.stop_bits = datalink_obj["stop_bits"].GetInt();
                    }
                    if (datalink_obj.HasMember("character_size"))
                    {
                        device.datalink.character_size = datalink_obj["character_size"].GetInt();
                    }
                    break;
                }
                default:
                    return false;
            }
            devices_.push_back(std::move(device));
        }

        return true;
    }
    catch (const std::exception& e)
    {
        return false;
    }
}

bool CZlBaseConfig::Save(const std::string& path)
{
    try
    {
        rapidjson::StringBuffer buffer;
        rapidjson::Writer<rapidjson::StringBuffer> writer(buffer);

        writer.StartObject();

        // devices
        writer.Key("devices");
        writer.StartArray();
        for (const auto& d : devices_)
        {
            // device
            writer.StartObject();
            writer.Key("number");
            writer.Int(d.number);
            writer.Key("count");
            writer.Int(d.count);
            writer.Key("type");
            writer.Int(d.type);

            // protocol uuid
            writer.Key("uuid");
            writer.String(d.uuid.c_str());

            // datalink
            writer.Key("datalink");
            writer.StartObject();
            writer.Key("type");
            writer.Int(d.datalink.type);
            writer.Key("is_shared");
            writer.Int(d.datalink.is_shared ? 1 : 0);

            writer.Key("localport");
            writer.Int(d.datalink.localport);
            writer.Key("remoteaddr");
            writer.String(d.datalink.remoteaddr);
            writer.Key("remoteport");
            writer.Int(d.datalink.remoteport);

            writer.Key("com");
            writer.String(d.datalink.com);
            writer.Key("baud_rate");
            writer.Int(d.datalink.baud_rate);
            writer.Key("flow_control");
            writer.Int(d.datalink.flow_control);
            writer.Key("parity");
            writer.Int(d.datalink.parity);
            writer.Key("stop_bits");
            writer.Int(d.datalink.stop_bits);
            writer.Key("character_size");
            writer.Int(d.datalink.character_size);
            // datalink
            writer.EndObject();
            // device
            writer.EndObject();
        }
        writer.EndArray();

        writer.EndObject();

        // save to file
        std::ofstream ofs;
        ofs.open(path);
        ofs.write(buffer.GetString(), buffer.GetSize());
        ofs.close();

        return true;
    }
    catch (const std::exception& e)
    {
        return false;
    }
}